package com.cloud.async.result.core.handler;

import com.cloud.async.business.body.FutureResult;
import com.cloud.async.business.core.AsyncTaskLifecycle;
import com.cloud.async.business.core.BaseAsyncResultHandler;
import com.cloud.async.error.ErrorHandler;
import com.cloud.async.flow.util.timer.SystemClock;
import com.cloud.async.monitor.ResultHandlerMonitor;
import com.cloud.async.result.core.body.ResultBody;
import com.cloud.async.util.ex.AsyncException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.lang.NonNull;

import java.util.concurrent.TimeUnit;
import java.util.function.Function;

public class StandardAsyncResultHandler extends BaseAsyncResultHandler {

    private final Log logger = LogFactory.getLog(getClass());

    private static final long INVALID_TIMESTAMP = -1L;

    public StandardAsyncResultHandler(Long localMilliSecond, AsyncTaskLifecycle asyncBusiness,
                                      ErrorHandler errorHandler, ResultHandlerMonitor resultHandlerMonitor) {
        super(localMilliSecond, asyncBusiness, errorHandler, resultHandlerMonitor);
    }


    @Override
    public <T> ResultBody<T> getResult(String taskId, Class<T> responseType) {
        return getResult(taskId, localMilliSecond, responseType);
    }

    @Override
    public <T> ResultBody<T> getResult(String taskId, Long timeoutMillis, Class<T> responseType) {
        long taskTimestamp = INVALID_TIMESTAMP;
        resultHandlerMonitor.onTaskStart(taskId);
        try {
            if (!isTaskActive(taskId)) {
                throw new AsyncException(String.format("Task %s not found or expired", taskId));
            }

            FutureResult asyncResult = getTaskResult(taskId);
            taskTimestamp = asyncResult.getTaskTimestamp();

            // 类型检查
            if (!asyncResult.getCls().equals(responseType)) {
                throw new AsyncException(
                        String.format("Type mismatch: expected=%s, actual=%s",
                                responseType.getName(), asyncResult.getCls().getName())
                );
            }

            // 获取结果并转换
            Object rsp = asyncResult.getTaskFuture().get(
                    timeoutMillis != null ? timeoutMillis : localMilliSecond,
                    TimeUnit.MILLISECONDS
            );

            if (rsp != null && !responseType.isInstance(rsp)) {
                throw new AsyncException(
                        String.format("Result type mismatch: expected=%s, actual=%s",
                                responseType.getName(), rsp.getClass().getName())
                );
            }
            ResultBody<T> result = new ResultBody<>(
                    responseType,
                    responseType.cast(rsp),
                    taskTimestamp,
                    SystemClock.now()
            );
            // 任务成功
            resultHandlerMonitor.onTaskComplete(taskId, result);
            return result;
        } catch (Exception e) {
//            logger.error(String.format("Failed to get result for taskId=%s, error: %s", taskId, e.getMessage(), e));
//            return new ResultBody<>(responseType, taskTimestamp, SystemClock.now(), e.getMessage());

            // 任务失败
            resultHandlerMonitor.onTaskFailed(taskId, e);
            return errorHandler.handleError(taskId, responseType, e);
        } finally {
            releaseTask(taskId);
        }
    }

    @Override
    public <T, R> ResultBody<R> getResult(String taskId, Long timeoutMillis, Class<T> responseType, @NonNull Function<T, R> converter) {
        ResultBody<T> rsp = getResult(taskId, timeoutMillis, responseType);
        return convertResult(rsp, converter);
    }

    @Override
    public <T, R> ResultBody<R> getResult(String taskId, Class<T> responseType, Function<T, R> converter) {
        ResultBody<T> rsp = getResult(taskId, localMilliSecond, responseType);
        return convertResult(rsp, converter);
    }

    /**
     * 公共逻辑提取：转换 ResultBody<T> 到 ResultBody<R>
     */
    private <T, R> ResultBody<R> convertResult(ResultBody<T> rsp, Function<T, R> converter) {
        if (rsp == null) {
            throw new IllegalStateException("ResultBody is null");
        }
        if (converter == null) {
            throw new IllegalArgumentException("Converter function must not be null");
        }
        if (rsp.isSuccess()) {
            R convertedResult = converter.apply(rsp.getResult());
            return new ResultBody(
                    rsp.getResponseType(),
                    convertedResult,
                    rsp.getExecutionTimestamp(),
                    rsp.getCurrentTimestamp()
            );
        } else {
            return new ResultBody(
                    rsp.getResponseType(),
                    rsp.getExecutionTimestamp(),
                    rsp.getCurrentTimestamp(),
                    rsp.getErrorMsg()
            );
        }
    }

}
